class Uploader {
  constructor(options = {}) {
    this.options = {
      progress: () => { },
      success: () => { },
      error: () => { },
      enctype: 'multipart/form-data',
      concurrency: 1, // Default concurrency limit
      ...options
    };
  }

  /**
   * Start the upload process
   * @param {Object} options - Upload options overriding defaults
   * @returns {Promise} Resolves when all uploads are complete
   */
  async upload(options = {}) {
    const opts = { ...this.options, ...options };
    if (!opts.data || !opts.data.length) return;

    await this._processQueue(opts);
  }

  /**
   * Process the upload queue with concurrency control
   * @param {Object} opts 
   */
  _processQueue(opts) {
    const files = opts.data;
    const total = files.length;
    let currentIndex = 0;
    let activeCount = 0;

    return new Promise((resolve) => {
      const next = () => {
        // If all files processed and no active uploads, we are done
        if (currentIndex >= total && activeCount === 0) {
          resolve();
          return;
        }

        // Start new uploads while we have files and capacity
        while (currentIndex < total && activeCount < opts.concurrency) {
          const index = currentIndex++;
          const file = files[index];
          activeCount++;

          this._uploadOne(file, index, opts)
            .finally(() => {
              activeCount--;
              next();
            });
        }
      };

      next();
    });
  }

  /**
   * Upload a single file
   * @param {File} file 
   * @param {number} index 
   * @param {Object} opts 
   */
  _uploadOne(file, index, opts) {
    return new Promise((resolve, reject) => {
      const formData = new FormData();
      // Maintain consistent naming convention: file0, file1, ...
      formData.append(`file${index}`, file);

      const startTime = Date.now();
      const xhr = new XMLHttpRequest();
      xhr.open("POST", opts.url, true);

      // Upload progress
      xhr.upload.addEventListener("progress", (e) => {
        if (e.lengthComputable) {
          const percentage = ((e.loaded / e.total) * 100).toFixed(2);
          opts.progress(index, percentage, e.loaded, e.total);
        } else {
          opts.progress(index, 0, 0, 0);
        }
      });

      // Upload complete
      xhr.onload = () => {
        const endTime = Date.now();
        const duration = endTime - startTime;

        if (xhr.status >= 200 && xhr.status < 300) {
          let response = xhr.responseText;
          try {
            // Attempt to parse JSON response
            const json = JSON.parse(response);
            response = json;
          } catch (e) {
            // Ignore parse error, use raw text
          }

          // Construct success object similar to original but safer
          const result = typeof response === 'object' && response ? response : { response };
          opts.success({ index, time: duration, ...result });
          resolve(response);
        } else {
          opts.error({ index, time: duration, status: xhr.status, statusText: xhr.statusText });
          reject(new Error(`Upload failed: ${xhr.status} ${xhr.statusText}`));
        }
      };

      // Network error
      xhr.onerror = () => {
        const endTime = Date.now();
        const duration = endTime - startTime;
        opts.error({ index, time: duration, status: 0, statusText: 'Network Error' });
        reject(new Error('Network error'));
      };

      xhr.send(formData);
    });
  }

  /**
   * Get preview URL for an image file input
   * @param {HTMLInputElement} fileNode 
   * @returns {string}
   */
  getPreviewImageURL(fileNode) {
    if (fileNode && fileNode.files && fileNode.files[0]) {
      return URL.createObjectURL(fileNode.files[0]);
    }
    return '';
  }
}

window.Uploader = Uploader;